home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / CIS_GAME.ARJ / QTIME1.THD < prev    next >
Text File  |  1993-07-01  |  14KB  |  274 lines

  1. ________________________ Subj: Resetting System Time ________________________
  2.  
  3. Fm: Mark Betz/GD SL 76605,2346                 # 204900 
  4. To: Darrell Fetzer 76636,241 (X)               Date: 25-Aug-92  22:47:17
  5.  
  6. Darrel, this method for calling the original int 8 handler at the proper rate
  7. for any counter value was passed on to me by a friend in CLMFORUM, Rud
  8. Merriam. Hope it's useful.
  9.  
  10. The easiest way to generate approximetly 18.2 pulses is to do the following:
  11.  
  12. 1. Create a long counter variable.
  13.  
  14. 2. At every tick of the 8254 add the count loaded into the 8254 to the
  15. counter. 
  16.  
  17. 3. If the counter exceeds 65k then call the old interrupt vector.
  18.  
  19. 4. Do a mod 65k on the counter to get the number of ticks over 65k.
  20.  
  21. That works for any value you put into the 8254 counter.
  22.  
  23.  
  24. ___________________ Subj: HTimer / High Resolution Timer ___________________
  25.  
  26. Fm: Mark Betz/GD SL 76605,2346                 # 194299 
  27. To: Sarwan Narine 76675,164                    Date: 01-Aug-92  14:39:41
  28.  
  29. Hi, Sarwan.
  30.  
  31. >> How do you program the timer
  32.  
  33. You do have a knack for these all-inclusive questions, don't you? <g>. Ok,
  34. prepare for an exhaustive discussion of the PC timer...On your motherboard
  35. there is either an 8254 timer chip, or it's equivalent embedded in a VLSI
  36. chipset. This chip actually consists of 3 seperate timer registers, and a
  37. status/input register. The three channels are 0, 1, and 2, and they are
  38. addressed at ports 0x40, 0x41, and 0x42 respectively. Port 0x43 is the
  39. status/input register, where you send commands or read the timer status. The
  40. timer channels are actually 16-bit counter registers. Channel 0 is the system
  41. timer tick, the one we're interested in. Channel 1 is the ram refresh pulse..
  42. don't mess with it. Channel 2 is hardwired to the PC speaker. It can be used
  43. to pump digitized sound to the speaker, but we're not getting into that
  44. today. The way that the counters work is simple: they are loaded with a
  45. value, and then they count down until the value reaches 0, at which time
  46. something happens. In the case of channel 1 that something is a system timer
  47. interrupt (int 08). The _rate_ at which the timers count down is 1.193180
  48. megahertz. This means that the counter value is decremented 1,193,180 times
  49. per second. The initial value loaded into the channel 1 register by the bios
  50. at startup is 0, which is immediately decremented to yield 65535. If you
  51. divide 1,193,180 / 65535 you get 18.2xxx, which is the approximate default
  52. rate of the system timer. One other consideration is very important. The
  53. timer channels operate in several modes. The default mode for channel 1 is
  54. square wave mode. This means that the timer is decremented by _2_ on each
  55. pulse, not 1. The first time it hits 0 it is reloaded, and the second time it
  56. hits 0 the interrupt occurs. 
  57.  
  58. Ok, there are some basic operations you need to do, in this order:
  59.  
  60.         * get the current timer mode so you can save it
  61.         * reprogram the timer to pulse mode
  62.         * load the counter with the right value (roughly 239 for a
  63.           5000 hz tick)
  64.         * reprogram the timer to it's original values on exit
  65.  
  66. I was going to attempt to post all the code here, but it's too long. So I'm
  67. going to email you one file, and suggest downloading two others. None of
  68. these will show you _exactly_ how to do what you want, but, taken together
  69. you should be able to figure it out. If you can't you shouldn't be messing
  70. with the timer anyway <g>. The one I'll mail is TIMER.ZIP, which contains
  71. some C code I threw together for the last guy who asked this question. The
  72. other two are TRANS.ZIP and HELPPC.ZIP in LIB 11. TRANS is a hi-res timing
  73. program I wrote for a compiled bimap competition we had some time ago. 
  74.  
  75.                                                         --Mark
  76. ...........................................................................
  77.  
  78. Editor's Note :  See the following files in GAMERS section 11
  79.  
  80. HTIMER.ZIP   33K 19-Nov-92  HTimer v1.2, hi-res timing class for BC++
  81. HTMRMS.ZIP   33K 19-Nov-92  HTimer v1.2, hi-res timing class for MSC++ 7.x
  82.  
  83. ...........................................................................
  84.  
  85. Fm: Bob Provencher/GD SL 71621,2632            # 245378 
  86. To: Mark Betz/Ass't SysOp 76605,2346 (X)       Date: 15-Nov-92  20:27:49
  87.  
  88. Hi Mark,
  89.  
  90. That HTIMER class is pretty wild!  You're doin some pretty neat stuff in
  91. there.  I didn't really expect to see anything and I didn't I did get some
  92. other wierd results.  If I do this (I tried the for loop)
  93.  
  94. while( !kbhit() )
  95. {
  96.      for ( ; timer.getElapsed() < cycle; )
  97.          ;
  98.      cout << timer.getElapsed() << timer.timerOff() << endl;
  99. }
  100.  
  101. The result I get are
  102.  
  103. 0 100120
  104.  
  105. And I still get some values below 100000.  Is there anything about
  106. getElapsed() that it can't be called twice in succession like that?
  107.  
  108. Bob
  109. ...........................................................................
  110.  
  111. Fm: Mark Betz/Ass't SysOp 76605,2346           # 245512 
  112. To: Bob Provencher/GD SL 71621,2632 (X)        Date: 15-Nov-92  23:53:29
  113.  
  114. I kind of got a kick out of doing the interface between the class and the
  115. hardware. That was the neatest part, for me. What I'd eventually like to do,
  116. after I get the core calculation engine working in all cases, is to extend
  117. the class so that you have a timer that returns mics, millis, hundredths,
  118. tenths, and seconds, as well as specialty timers, like one that counts a
  119. certain amount of time and then makes a callback to your app.
  120.  
  121. I've been able to show that the problem shows up in getElapsed and timerOff.
  122. The error is taking place in calcElapsed. What happens is that it
  123. occasionally returns too large a value. For example, in this code:
  124.  
  125.     timer.timerOn();
  126.     while (time < 100000L) {
  127.        time = timer.getElapsed();
  128.     }
  129.     cout << timer.timerOff() << '\n';
  130.  
  131. what happens is that you usually exit the while() loop when time > 100000,
  132. something like 100025. Occasionally getElapsed will return an absurd value,
  133. like 142000, which bounces you out of the loop. Then timerOff() is called and
  134. it gets the correct count again, which might be 65000, or whatever. This is
  135. what was causing the original problem that Jesse saw. This only happens when
  136. calcElapsed is dealing with a rollover in the counter register; i.e., when
  137. ticks > 0. At the hardware level, the max mics you can count before a
  138. register rollover takes place is 65535/1.193, or 54932. Since the register
  139. can be anywhere in it's count when a timer starts up, this is often much less
  140. for the first cycle. That's why I hooked int 8 and provided a rolling tick
  141. counter. Somewhere in the interaction between ticks and register counts I'm
  142. getting a bogus value. 
  143. ...........................................................................
  144.  
  145. Fm: Mark Betz/Ass't SysOp 76605,2346           # 245566 
  146. To: Bob Provencher/GD SL 71621,2632 (X)        Date: 16-Nov-92  01:58:55
  147.  
  148. I've got it!! I don't know how to fix it yet, but I found the problem. I owe
  149. Jesse one for writing code that caused this problem to surface. It's an
  150. extremely subtle bug that strikes pseudo-randomly (I say pseudo, because it's
  151. dependent on the resonances between the speed of execution of a polling loop,
  152. and the speed of the timer chip). Btw, I have to say, with reddened face,
  153. that I damn well should have caught this myself. It'll show up in any long
  154. series of short timing cycles. In longer timing cycles it will tend to get
  155. buried. 
  156.  
  157. This'll be tricky to explain, but I'll give it a shot, since I'd like some
  158. ideas on how to prevent it. It's really a concurrency problem. The problem
  159. arises out of the constraints of the timer chip counter registers. Imagine
  160. the count value in the 16-bit counter along a time line:
  161.  
  162. count:     65535              32767                   0
  163.            |____________________|_____________________|
  164.            |                                          |
  165. sys event: start cycle                           int 08
  166.  
  167. The timer causes an interrupt 8 at "terminal count" ( 0 ), then reloads the
  168. counter register with 65535 (actually 0 in the default case, which is
  169. immediately decremented to either 65535 in mode 3, or 65534 in mode 2, before
  170. counting resumes) and starts over. The whole process is driven at 1.193180
  171. Mhz, so that you have the counter register decremented 1,193,180 times per
  172. second. The default frequency for the system timer interrupt, btw, is derived
  173. from 1,193,180 hz./65535 = 18.2 hz., which is the number of times the process
  174. reaches terminal count in one second.
  175.  
  176. You do hi-res timing on the PC by watching the counter register. Each
  177. decrement in the register is equal to approximately 800 nanoseconds. You
  178. latch the register count when you start the timer, and latch it again when
  179. you finish timing. If you're lucky the timeline looks like this:
  180.  
  181. count:     65535 55432        32767     20013         0
  182.            |_____|______________|_______|_____________|
  183.                  |                      |
  184. timer:           start                  finish
  185.  
  186. In this case the calculation is simple: microseconds = (start-finish)/1.193.
  187. Where you run into trouble is when this happens:
  188.  
  189. count:     65535 55432        32767     20013         0
  190.            |_____|______________|_______|_____________|
  191.                  |                      |
  192. timer:           finish                 start
  193.  
  194. In this case we grabbed the counter when it was at 20013. While we were
  195. timing it wrapped around, and when we grabbed it again it was at 55432. The
  196. calculation in this case is microseconds = start + (65535-finish)/1.193. One
  197. more level of additional complexity exists when the timer wraps more than
  198. once during a timing session (a "session" is defined as the time between
  199. calls to either getElapsed or timerOff). In both of these cases you can see
  200. that the only way for the program to know if the counter has wrapped is to
  201. watch the timer interrupt. We _know_ that each timer interrupt signals that
  202. the counter has wrapped. 
  203.  
  204. Now that I've laid the foundation, here's the bug:
  205.  
  206. count:     65535     43345    32767                 2 0
  207.            |___________|________|___________________|_|
  208.                        |                            |
  209. timer:                 start                   finish
  210.  
  211. The program latches the finishing counter value, 2 in this case. However,
  212. before it can calculate the elapsed time the counter reaches terminal count
  213. and issues an interrupt. Now the program thinks there was a wrap in the
  214. counter, when there actually wasn't one during the timing interval: it
  215. occured immediately after.
  216.  
  217. One possible solution is to "lock" the tick counter, so that it cannot be
  218. modified by the interrupt handler while an elapsed time is being calculated.
  219.  
  220. The problem with this approach is that you can't sit in the interrupt handler
  221. waiting for the app to finish so you can update the tick counter. The best
  222. you can do is set a flag in the interrupt handler so that it will remember to
  223. update the tick counter on the next pass. This isn't workable, since the next
  224. opportunity to update the tick counter will be 54,945 microseconds away, and
  225. that's unacceptable resolution for the timer class. 
  226.  
  227. I apologize for the very long message, but it's a highly technical, and I
  228. think very interesting problem. If anyone has any ideas I'd love to hear
  229. them. I need to be able to let the tick counter run free, but somehow
  230. guarantee that it is valid for a particular calculation of elapsed time.
  231.  
  232. Ahh, a possible solution just hit me. If the interrupt handler sees that an
  233. elapsed time is being calculated, it sets a "waiting" flag, instead of
  234. incrementing the tick counter, and then IRETS. In the HTimer::calcElapsed
  235. function the code checks the waiting flag after it's done with it's
  236. calculation, and updates the tick counter itself if the flag is set. What do
  237. you think?
  238.  
  239.                                                         --Mark
  240. ...........................................................................
  241.  
  242. Fm: Mark Betz/Ass't SysOp 76605,2346           # 246084 
  243. To: Bob Provencher/GD SL 71621,2632 (X)        Date: 16-Nov-92  22:58:23
  244.  
  245. Hi. That's a universal truth of bugs <g>. The reason why I didn't catch that
  246. error was related to the test suite. The tests I created have one fatal flaw:
  247. they're each performed once! When I saw the correct result I thought,
  248. "Yippee, it works", and went on about my business. Now I find that there's an
  249. error that occurs as rarely as once in one thousand cycles. Repetitive
  250. testing would have caught it. I learned a lesson, and luckily I got to learn
  251. it on a relatively obscure piece of PD code. If it had been something mission
  252. critical I would have been burned, or someone else would have, perhaps
  253. literally. I can take comfort in the fact that, in a mission-critical
  254. project, someone else would have found it and fired me <g>.
  255.  
  256. Btw, I tried locking the tick counter, and it improved the situation, but the
  257. problem didn't go away. I found that I could eliminate it for the end of the
  258. timing cycle by moving some assignments so that they occured differently with
  259. relation to the latching of the counter. I also found that the same problem
  260. was occuring at the start of the timer cycle, in timerOn(). This has proven
  261. tougher to deal with, but I think I have it subdued for the moment using a
  262. test for the error condition (an approach I detest, but I'll use it until
  263. something else works).
  264.  
  265. The whole thing makes a pretty interesting test case of a real-time
  266. hardware/software interface problem. I'll upload the current version asap,
  267. and perhaps we can bang on it enough to make it puke again. I'd like to get
  268. to the point where HTimer is a 100% solid concurrent multiple-channel timing
  269. object. 
  270.                                                         --Mark
  271. ...........................................................................
  272.  
  273.  
  274.